package network_programming.np2_nonblocking_communication;

import java.nio.channels.ServerSocketChannel;

/**
 * 非阻塞通信
 *
 * java.nio.channel包提供了支持非阻塞通信的类
 *
 * ServerSocketChannel类             Channel（通道）
 * 是ServerSocket的替代类，支持阻塞通信与非阻塞通信，是SelectableChannel的子类
 * SelectableChannel类及其子类都能委托Selector来控制它们可能发送的一些事件，这种委托过程也被称为注册事件过程
 *
 * SocketChannel类
 * 是Socket的替代类，支持阻塞通信与非阻塞通信，是SelectableChannel的子类
 *
 * Selector
 * 为ServerSocketChannel监控接收连接就绪事件，为SocketChannel监控连接就绪、读就绪和写就绪事件
 *
 * SelectorKey
 * 代表ServerSocketChannel以及SocketChannel向Selector注册事件的句柄
 * 当一个SelectorKey对象位于Selector对象的selected-keys集合中，就表示与这个SelectorKey对象相关的时间发生了
 *
 *
 * ServerSocketChannel向Selector注册接收连接就绪事件的代码：
 * SelectionKey key = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
 *
 *
 * SelectionKey类
 * 表示事件类型的静态常量
 * ServerSocketChannel只可能发送一种事件
 *      SelectionKey.OP_ACCEPT      接收连接就绪事件，表示至少有了一个客户连接，服务器可以接收这个连接
 * SocketChannel可能发送以下3种事件
 *      SelectionKey.OP_CONNECT     连接就绪事件，表示客户与服务器的连接已经建立成功
 *      SelectionKey.OP_READ        读就绪事件，表示输入流中已经有了可读数据，可以执行读操作了
 *      SelectionKey.OP_WRITE       写就绪事件，表示已经可以向输出流写数据了
 *
 *
 * SocketChannel提供了接收和发送数据的方法
 * read(ByteBuffer buffer)          接收数据，将接收到的数据存放到参数指定的ByteBuffer中
 * write(ByteBuffer buffer)         将参数指定的ByteBuffer中的数据发送出去
 *
 *
 * ByteBuffer类      表示字节缓冲区，继承于Buffer类
 *                  转换为字符串时需要用到Charset类，Charset类代表字符编码，
 *                  Charset类提供把字节流转换为字符串（解码）和把字符串转换为字节流（编码）的方法
 *
 *
 * Buffer       缓冲区
 * 从两个方面提高I/O操作的效率
 *      减少实际的物理读写次数
 *      缓冲区在创建时被分配内存，这块内存区域一直被重用，这可以减少动态分配和回收内存区域的次数
 * java.nio.Buffer类是一个抽象类，不能被实例化，有8个具体的缓冲区，最基本的缓冲区是ByteBuffer，存放得数据单元是字节
 *
 * 属性
 * capacity     容量，表示缓冲区可以保存多少数据
 * limit        极限，表示缓冲区的当前终点，不能对缓冲区中超过极限的区域进行读写操作，
 *              极限是可被修改的，这有利于缓冲区重用，是一个非负整数，不应该大于容量
 * position     位置，表示缓冲区中下一个读写单元的位置，
 *              每次读写缓冲区的数据时，该值都会改变，为下一次读写数据做准备，不应该大于极限
 * 容量 >= 极限 >= 位置 >= 0
 *
 * 改变属性的方法
 * clear()      把极限设置为容量，把位置设置为0
 * flip()       把极限设置为位置，把位置设置为0
 * rewind()     不改变极限，把位置设置为0
 * public final Buffer position(int newPosition)
 *              设置缓冲区的位置。如果标记已定义且大于新位置，则丢弃它。
 *              新位置值：必须非负且不大于当前极限
 *              返回：这个缓冲区
 *
 * public final int position()  返回缓冲区的位置
 * remaining()  返回缓冲区的剩余容量，取值等于极限-位置
 * compact()    删除缓冲区内从0到当前位置position的内容，
 *              把当前位置position到极限limit的内容拷贝到0~(limit-position)的区域，当前位置和极限的取值也做相应变化
 *
 *
 * ByteBuffer类
 * 没有公开的构造方法，但提供了两个获取ByteBuffer实例的静态工厂方法
 * allocate(int capacity)           返回一个ByteBuffer对象，参数capacity指定缓冲区的容量
 * directAllocate(int capacity)     返回一个ByteBuffer对象，参数capacity指定缓冲区的容量
 *                                  该方法返回的缓冲区被称为直接缓冲区，它与当前操作系统能够更好地耦合
 *                                  能进一步提高I/O操作的速度，但分配直接缓冲区的系统开销很大
 *                                  只有在缓冲区较大并且长期存在或需要经常重用时才使用这种缓冲区
 *
 *
 * 所有缓冲区类都提供读写缓冲区的方法
 * get()            相对读，从缓冲区的当前位置读取一个单元的数据，读完后位置加1
 * get(int index)   绝对读，从参数index指定的位置读取一个单元的数据
 * put(单元数据类型 data)             相对写，向缓冲区的当前位置写入一个单元的数据，写完后位置加1
 * put(int index, 单元数据类型 data)  绝对写，向阐述index指定的位置写入一个单元的数据
 *
 * ByteBuffer类不仅可以读写一个单元的字节，还可以读写基本数据类型的数据，如：
 * getInt()
 * getInt(int index)
 * putInt(int value)
 * putInt(int index, int value)
 * getChar()
 * getChar(int index)
 * putChar(char value)
 * putChar(int index, char value)
 *
 * ByteBuffer类类提供了获取缓冲区视图的方法
 * ShortBuffer asShortBuffer()
 * CharBuffer asCharBuffer()
 * IntBuffer asIntBuffer()
 * FloatBuffer asFloatBuffer()
 *
 * 获取ByteBuffer的CharBuffer缓冲区视图：
 * CharBuffer charBuffer = bytBuffer.asCharBuffer();
 *
 * 以上CharBuffer视图和底层ByteBuffer共享同样的数据，修改CharBuffer视图的数据会反映到底层ByteBuffer
 * CharBuffer视图和底层ByteBuffer有各自独立的位置、极限、容量属性
 *
 *
 * Charset          字符编码
 * java.nio.Charset类的每个实例代表特定的字符编码类型
 *
 * 编码与解码的方法
 * ByteBuffer encode(String str)    对参数str指定的字符串进行编码，把编码结果存放在ByteBuffer对象中，并将其返回
 * ByteBuffer encode(CharBuffer cb) 对参数cb指定的字符进行编码，把编码结果存放在ByteBuffer对象中并将其返回
 * CharBuffer decode(ByteBuffer bb) 对参数bb指定的字节序列进行解码，把解码结果存放在一个CharBuffer对象中并将其返回
 *
 * Charset类的静态方法
 * forName(String encode)       返回一个Charset对象，代表参数encode指定的编码类型
 *          Charset charset = Charset.forName("GBK");
 * defaultCharset()             返回代表本地平台默认的字符编码Charset对象
 *
 *
 *
 * Channel      接口，表示通道，用来连接缓冲区与数据源或数据汇（即数据目的地）
 * java.nio.channels.Channel接口只声明了两个方法
 * close()      关闭通道
 * isOpen()     判断通道是否打开
 * 通道在创建时被打开，一旦关闭通道就不能重新打开它
 *
 * Channel接口的两个最重要的子接口
 * ReadableByteChannel接口
 *      read(ByteBuffer dst)    该方法把数据源的数据读入参数指定的ByteBuffer缓冲区中
 * WritableByteChannel接口
 *      write(ByteBuffer src)   该方法把参数指定的ByteBuffer缓冲区中的数据写到数据汇中
 *
 * ByteChannel接口扩展了ReadableByteChannel和WritableByteChannel接口，因而同时支持读写操作
 *
 *
 *
 * SelectableChannel类，是一种支持阻塞I/O和非阻塞I/O的通道
 * 主要方法
 * public SelectableChannel configureBlocking(boolean block) throws IOException
 *      当block为ture时表示把SelectableChannel设置为阻塞模式，当block为false时表示把SelectableChannel设为非阻塞模式
 *      默认情况下SelectableChannel采用阻塞模式
 *      该方法返回SelectableChannel对象本身的引用，相当于“return this”
 *
 * public boolean isBlocking()      判断SelectableChannel是否处于阻塞模式
 *
 * public SelectionKey register(Selector sek, int ops) throws ClosedChannelException
 *                                  向Selector注册事件
 * public SelectionKey register(Selector sek, int ops, Object attachment) throws ClosedChannelException
 *                                  向Selector注册事件
 *      返回值SelectionKey类对象       用来跟踪被注册的时间
 *      attachment      用于为SelectionKey关联一个附件，当被注册事件发生后，需要处理该事件时，
 *                      可以从SelectionKey中获得该附件，该附件可用来包含与处理这个事件相关的信息
 *
 * socketChannel（SelectableChannel的一个子类）向Selector注册读就绪和写就绪事件
 * SelectionKey key = socketChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
 *
 * MyHandler handler = new NyHandler();     // 负责处理事件的对象
 * SelectionKey key = socketChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE, handler);
 * 等价于
 * SelectionKey key = socketChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
 * MyHandler handler = new NyHandler();
 * key.attach(handler);     // 关联一个附件
 *
 *
 * ServerSocketChannel类
 * 是ServerSocket的替代类
 * 从SelectableChannel中继承了configureBlocking()和register()方法
 * 没有public类型的构造方法，必须通过其静态方法open()来创建ServerSocketChannel对象
 * 每一个ServerSocketChannel对象都与一个ServerSocket对象关联，其socket()方法返回与其关联的ServerSocket对象
 * serverSocketChannel.socket().bind(port)      把服务器进程绑定到一个本地端口
 *
 * 静态工厂方法
 * public static ServerSocketChannel open() throws IOException
 *              返回一个没有与本地任何端口绑定的ServerSocketChannel对象，并且该对象处于阻塞模式
 * public SocketChannel accept() throws IOException     类似ServerSocket的accept()方法，等待接收客户连接
 *              如果ServerSocketChannel处于非阻塞模式，当没有客户连接时就立即返回null，
 *              若有客户连接时则就会一直阻塞，直到有客户连接就绪或出现IOException
 *              该方法返回的SocketChannel对象处于阻塞模式，设为非阻塞模式：socketChannel.configureBlocking(false);
 * public final int validOps()      返回ServerSocketChannel所能产生的时间，该方法总是返回SelectionKey.OP_ACCEPT
 * public ServerSocket socket()     返回ServerSocketChannel关联的ServerSocket对象
 *
 *
 * SocketChannel类
 * 是Socket的替代类，继承SelectableChannel，实现ByteChannel接口
 *
 * 静态工厂方法
 * public static SocketChannel open() throws IOException
 * public static SocketChannel open(SocketAddress remote) throws IOException
 *              带参数的静态工厂方法还会与远程服务器建立连接
 *      SocketChannel socketChannel = SocketChannel.open();
 *      socketChannel.connect(remote);
 * open()方法返回的SocketChannel对象处于阻塞模式，设为非阻塞模式：socketChannel.configureBlocking(false);
 *
 * public Socket socket()           返回与该SocketChannel关联的Socket对象
 * public boolean isConnected()     判断底层Socket是否已经建立远程连接
 * public boolean isConnectionPending()     判断是否正在进行远程连接
 * public boolean connect(SocketAddress remote) throws IOException      使底层Socket建立远程连接
 *          非阻塞模式：如果立刻连接成功则返回true，否则返回false，程序稍后必须通过调用finishConnect()方法来完成连接
 *          阻塞模式：如果立刻连接成功则返回true，否则进入阻塞状态，直到连接成功，或出现I/O异常
 * public boolean finishConnect() throws IOException    试图完成连接远程服务器的操作
 *          非阻塞模式：建立连接从调用SocketChannel的connect()方法开始，到调用finishConnect()方法结束
 *                    如果顺利完成连接或在调用此方法之前已建立连接，则该方法返回true，如果连接还没完成则返回false
 *          阻塞模式：如果连接操作还没完成，则会进入阻塞状态，直到连接完成，或出现I/O异常
 * public int read(ByteBuffer dst) throws IOException   从Channel中读入若干字节并将其存放到参数指定的ByteBuffer中
 *                                                      返回实际上读入的字节数，返回“-1”则表示读到了输入流的末尾
 * public int write(ByteBuffer src) throws IOException  把参数src指定的ByteBuffer中的字节写到Channel中
 *
 *
 *
 * Selector 抽象类
 * 一个Selector对象中包含3种类型的SelectionKey的集合
 *      all-keys集合：当前所有向Selector注册的SelectionKey的集合，Selector的keys()方法返回该集合
 *      selected-keys集合：相关事件已经被Selector捕获的SelectionKeys的集合，Selector的selectedKeys()方法返回该集合
 *      cancelled-keys集合：已被取消的SelectionKey的集合，Selector没有提供访问这种集合的方法
 *
 *      selected-keys、cancelled-keys集合都是all-keys集合的子集
 *
 * 执行SelectableChannel的 register() 方法时，该方法新建一个SelectionKey并将其加入Selector的all-keys集合中
 *
 * 静态工厂方法
 * public static Selector open() throws IOException
 *
 * 实例方法
 * public boolean isOpen()      判断Selector是否处于打开状态
 * public Set<SelectionKey> keys()  返回所有与Selector关联的SelectionKey对象集合
 * public int selectNow() throws IOException    返回相关时间已经放生的SelectionKey对象的数目
 *      该方法采用非阻塞工作方式时返回当前相关事件已经发生的SelectionKey对象的数目，如果没有则立即返回0
 *
 * public int select() throws IOException
 * public int select(long timeout) throws IOException
 *      返回相关事件已经发生的SelectionKey对象的数目，如果一个也没有则进入阻塞状态，直至出现以下情况之一就从该方法中返回：
 *          1、至少有一个SelectionKey的相关事件已经发生
 *          2、其他线程调用了Selector的wakeup()方法，导致执行select()方法的线程立即从select()方法中返回
 *          3、当前执行select()方法的线程被其他线程中断
 *          4、超出了等待时间。带参数的select()方法的参数timeout设定等待时间，单位为ms。调用不带参数的方法则永远不会超时
 *
 * public Selector wakeup()     唤醒执行Selector的select()方法（也适用于select(long timeout)方法）的线程
 * public void close() throws IOException       关闭Selector
 *      如果有其他线程正在执行这个Selector的select()方法并处于阻塞状态，执行close()方法时会使这个线程立即返回
 *      close()方法使得Selector占用的所有资源都被释放，所有与Selector关联的SelectionKey都被取消
 *
 *
 *
 * SelectionKey类
 * 该类对象是用来跟踪注册事件的句柄
 * 在以下情况下SelectionKey对象失效
 *      1、程序调用SelectionKey的cancel()方法
 *      2、关闭与SelectionKey关联的Channel
 *      3、与SelectionKey关联的Selector被关闭
 *
 * SelectionKey中定义了4种事件，分别用4个int类型常量来表示
 *      SelectionKey.OP_ACCEPT：接收连接就绪事件，表示服务器监听到了客户连接，服务器可以接收这个连接了，常量值为16
 *      SelectionKey.OP_CONNECT：连接就绪事件，表示客户与服务器的连接已经建立成功，常量值为8
 *      SelectionKey.OP_READ：读就绪事件，表示通道中已经有了可读数据，可可执行读操作了，常量值为1
 *      SelectionKey.OP_WRITE：写就绪事件，表示已经可以向通道写数据了，常量值为4
 * 以上4种事件常量分别占据不同的二进制位，可通过二进制的或运算符“|”来进行任意组合
 *
 * 所有感兴趣的事件：SelectionKey的不带参数的interestOps()方法返回所有感兴趣的事件
 *      假定返回值为SelectionKey.OP_READ | SelectionKey.OP_WRITE，表示这个SelectionKey对读就绪和写就绪事件感兴趣
 * 指定SelectionKey感兴趣的事件：
 *      SelectionKey key = socketChannel.register(selector, SelectionKey.OP_CONNECT | SelectionKey.OP_READ);
 *      key.interestOps(SelectionKey.OP_WRITE);     // 添加一个感兴趣的事件
 *
 * 所有已经发生的事件：SelectionKey的readyOps()方法返回所有已经发生的事件
 *      假定返回值为SelectionKey.OP_READ | SelectionKey.OP_WRITE，表示读就绪和写就绪事件已经发生了，
 *      与之关联的SocketChannel可进行读写操作了
 *
 * 主要方法
 * public SelectableChannel channel()   返回与这个SelectionKey对象关联的SelectableChannel对象
 * public Selector selector()   返回与这个SelectionKey对象关联的Selector对象
 * public boolean isValid()     判断这个SelectionKey是否有效
 * public void cancel()         使SelectionKey对象失效
 * public int interestOps()     返回这个SelectionKey感兴趣的事件
 * public SelectionKey interestOps(int ops)     为SelectionKey添加感兴趣的事件
 *                  该方法返回当前SelectionKey对象本身的引用，相当于“return this”
 * public int readyOps()        返回已经就绪的事件
 * public final boolean isReadable()    判断与之关联的SocketChannel的读就绪事件是否已经发生
 *                  该方法等价于： key.readyOps() & SelectionKey.OP_READ != 0
 * public final boolean isWritable()    判断与之关联的SocketChannel的写就绪事件是否已经发生
 *                  该方法等价于： key.readyOps() & SelectionKey.OP_WRITE != 0
 * public final boolean isConnectable()    判断与之关联的SocketChannel的连接绪事件是否已经发生
 *                  该方法等价于： key.readyOps() & SelectionKey.OP_CONNECT != 0
 * public final boolean isAcceptable()    判断与之关联的SocketChannel的接收连接绪事件是否已经发生
 *                  该方法等价于： key.readyOps() & SelectionKey.OP_ACCEPT != 0
 * public final Object attach(Object obj)   使SelectionKey关联一个附件
 *
 *
 *
 * Channels类
 * 提供通道与传统的基于I/O的流、Reader、Writer之间进行转换的静态方法
 * ReadableByteChannel newChannel(InputStream in)       输入流转换成读通道
 * WritableByteChannel newChannel(OutputStream out)     输出流转换成写通道
 * InputStream newInputStream(AsynchronousByteChannel ch)   异步通道转换成输入流
 * InputStream newInputStream(ReadableByteChannel ch)       读通道转换成输入流
 * OutputStream newOutputStream(AsynchronousByteChannel ch) 异步通道转换成输出流
 * OutputStream newOutputStream(WritableByteChannel ch)         写通道转换成输出流
 * Reader newReader(ReadableByteChannel ch, String csName)      读通道转换成Reader，参数csName指定字符编码
 * Reader newReader(ReadableByteChannel ch, Charset charset)    读通道转换成Reader，参数csName指定字符编码
 * Reader newReader(ReadableByteChannel ch, CharsetDecoder dec, int minBufferCap)
 *              读通道转换成Reader，参数dec指定字符解码器，参数minBufferCap指定内部字节缓冲区的最小容量
 * Writer newWriter(WritableByteChannel ch, String csName)      写通道转换成Writer，参数csName指定字符编码
 * Writer newWriter(WritableByteChannel ch, Charset charset)    写通道转换成Writer，参数charset指定字符编码
 * Writer newWriter(WritableByteChannel ch, CharsetEncoder enc, int minBufferCap)
 *              写通道转换成Writer，参数enc指定字符编码器，参数minBufferCap指定内部字节缓冲区的最小容量
 *
 * 将SocketChannel转换成输入流
 * SocketChannel socketChannel = serverSocketChannel.accept();
 * InputStream in = Channels.newInputStream(socketChannel);
 *
 *
 *
 * NetworkChannel接口
 * 提供设置和读取各种Socket选项的方法
 *
 * <T> T getOption(SocketOption<T> name)        获取特定的Socket选项值
 * <T> NetworkChannel setOption(SocketOption<T> name, T value)      设置特定的Socket选项
 * Set<SocketOption<?>> supportedOption()       获取所有支持的Socket选项
 *
 *
 *
 * StandardSocketOptions类
 * 提供以下表示特定选项的常量
 * SocketOption<NetworkInterface>       StandardSocketOptions.IP_MULTICAST_IF
 * SocketOption<Boolean>                StandardSocketOptions.IP_MULTICAST_LOOP
 * SocketOption<Integer>                StandardSocketOptions.IP_MULTICAST_TTL
 * SocketOption<Integer>                StandardSocketOptions.IP_TOS
 * SocketOption<Boolean>                StandardSocketOptions.SO_BROADCAST
 * SocketOption<Boolean>                StandardSocketOptions.SO_KEEPALIVE
 * SocketOption<Integer>                StandardSocketOptions.SO_LINGER             关闭时存留时间
 * SocketOption<Integer>                StandardSocketOptions.SO_RCVBUF
 * SocketOption<Boolean>                StandardSocketOptions.SO_REUSEADDR
 * SocketOption<Boolean>                StandardSocketOptions.SO_REUSEPORT
 * SocketOption<Integer>                StandardSocketOptions.SO_SNDBUF
 * SocketOption<Boolean>                StandardSocketOptions.TCP_NODELAY
 *
 *
 * // 设置SocketChannel的SO_LINGER（关闭时存留时间）选项为300s
 * SocketChannel socketChannel = SocketChannel.open();
 * socketChannel.setOption(StandardSocketOptions.SO_LINGER, 300);
 * */
public class NonblockingCommunication {
    int i;
    ServerSocketChannel serverSocketChannel;

    public void NonblockingCommunication() {
        System.out.println("ggg");
    }

    public static void main(String[] args) {
        NonblockingCommunication n = new NonblockingCommunication();
        System.out.println(n.i);
        System.out.println(n.serverSocketChannel);
        n.NonblockingCommunication();
    }
}
